﻿using Microsoft.Extensions.Caching.Memory;

namespace WebApplication1.CacheHelper
{
    public class MemoryCacheHelper : IMemoryChcheHelper
    {
        private readonly IMemoryCache _memoryCache;

        public MemoryCacheHelper(IMemoryCache memoryCache)
        {
            _memoryCache = memoryCache;
        }

        private static void ValidateValueType<TResult>()
        {
            Type typeResult = typeof(TResult);
            if ((typeResult.IsGenericType))
            {
                typeResult = typeResult.GetGenericTypeDefinition();
            }
            if (typeResult == typeof(IEnumerable<>) || typeResult == typeof(IAsyncEnumerable<TResult>) || typeResult == typeof(IQueryable<TResult>) || typeResult == typeof(IQueryable))
            {
                throw new InvalidOperationException("please use List<T> or T[] instead");
            }
        }

        /// <summary>
        /// 该方法用于设置缓存项的随机过期时间。它接受一个 ICacheEntry 实例和一个基础过期时间 baseExpireSeconds，然后在基础过期时间的范围内生成一个随机的过期时间，并将其设置为缓存项的绝对过期时间。
        /// </summary>
        /// <param name="entry"></param>
        /// <param name="baseExpireSeconds"></param>
        private static void InitCacheEntity(ICacheEntry entry, int baseExpireSeconds)
        {
            double sec = Random.Shared.Next(baseExpireSeconds, baseExpireSeconds * 2);
            TimeSpan expirationTime = TimeSpan.FromSeconds(sec);
            entry.AbsoluteExpirationRelativeToNow = expirationTime;
        }

        public TResult? GetOrCreate<TResult>(string cacheKey, Func<ICacheEntry, TResult?> valueFactory, int expirationTime = 60)
        {
           ValidateValueType<TResult>();
            if (!_memoryCache.TryGetValue(cacheKey, out TResult result))
            {
                using ICacheEntry entry = _memoryCache.CreateEntry(cacheKey);
                InitCacheEntity(entry, expirationTime);
                result = valueFactory(entry);
                entry.Value = result;
            }
            return result;
        }

        public async Task<TResult?> GetOrCreateAsync<TResult>(string cacheKey, Func<ICacheEntry, Task<TResult?>> valueFactory, int expirationTime = 60)
        {
            ValidateValueType<TResult>();
            if (!_memoryCache.TryGetValue(cacheKey, out TResult result))
            {
                using ICacheEntry entry = _memoryCache.CreateEntry(cacheKey);
                InitCacheEntity(entry, expirationTime);
                result = await valueFactory(entry);
                entry.Value = result;
            }
            return result;
        }

        public void Remove(string cacheKey)
        {
            _memoryCache.Remove(cacheKey);
        }
    }
}
